<?php
/**
 *      [迷你云] (C)2009-2012 南京恒为网络科技.
 *   软件仅供研究与学习使用，如需商用，请访问www.miniyun.cn获得授权
 * 
 */
?>
<?php

class MBtUpload extends MApplicationComponent {
    
    private $description;
    
    public function invoke() {
        parent::init();
                $if_match   = @$_SERVER['HTTP_IF_MATCH'];
        $clength    = @$_SERVER['CONTENT_LENGTH'];
        $crange     = @$_SERVER['HTTP_CONTENT_RANGE'];
        $total_size = @$_SERVER['HTTP_X_CONTENT_LENGTH'];
        $block_size = @$_SERVER['HTTP_X_BLOCK_LENGTH'];
                                $user       = MUserManager::getInstance ()->getCurrentUser ();
        $space      = MActionFilter::handleGetUserSpace($user->user_id);
        $used_space = MActionFilter::handleGetUsedSapce($user->user_id);
        
        $used_space += $total_size;
        if ($used_space > $space) {
            throw new MFilesException(FXP::t("User is over storage quota."), 
            MConst::HTTP_CODE_507);
        }
                                $this->cache = DOCUMENT_CACHE . MUtils::getPathBySplitStr($if_match);
        $des_path    = DOCUMENT_CACHE . MUtils::getPathBySplitStr($if_match) . ".des";
        $store_path  = DOCUMENT_ROOT_BLOCK . MUtils::getPathBySplitStr($if_match);
                if (file_exists($store_path)) {
            $this->cache = $store_path;
            return true;
        }
        
        $this->description = $this->hanleDescription($des_path, $this->cache, $total_size, $block_size, $if_match);
                                if ($clength == 0 && preg_match ( "/^bytes \*\/([0-9]+)/i", $crange, $match )) {
            if ($this->handleCheckCompleted() == true) {
                                $this->handleCheck($this->cache, $if_match, $store_path,$des_path);
                $this->cache = $store_path;
                return true;
            }
            $this->handleAssigns();
        }
        
                                if (!preg_match ( "/^bytes ([0-9]+)-([0-9]+)/i", $crange, $match )) {
            throw new MFilesException ( FXP::t ( MConst::PARAMS_ERROR ), MConst::HTTP_CODE_400 );
        }
        
        $start = $match[1];
        $end   = $match[2];
        $index = $start / $block_size;
                                
        if ($this->description['blocks'][$index] != '1') {
            $this->handleWriteBytes($this->cache, $start, $clength);
            $this->description['blocks'][$index] = "1";
            file_put_contents($des_path, serialize($this->description));
        }
        
                                if ($this->handleCheckCompleted() == false) {
            $this->handleAssigns();
        }
        
                $this->handleCheck($this->cache, $if_match, $store_path, $des_path);
        $this->cache = $store_path;
    }
    
    
    private function handleCheckCompleted() {
        $completed = true;
                                for ($i = 0; $i < strlen($this->description['blocks']); $i++) {
            if ($this->description['blocks'][$i] == "0") {
                $completed = false;
                break;
            }
        }
        return $completed;
    }
    
    
    
    private function handleCheck($cache, $hash, $store_path, $des_path) {
        $signature = MUtils::getFileSha1($cache);
        if ($hash != $signature) {
            unlink($des_path);
            throw new MFilesException ( FXP::t ( MConst::PARAMS_ERROR ), MConst::HTTP_CODE_400 );
        }
                if (!MUtils::MkDirs(dirname($store_path))) {
            throw new MFilesException ( FXP::t ( MConst::INTERNAL_SERVER_ERROR ), MConst::HTTP_CODE_500 );
        }
                if (copy($cache, $store_path) == FALSE) {
            throw new MFilesException ( FXP::t ( MConst::PARAMS_ERROR ), MConst::HTTP_CODE_400 );
        }
        unlink($cache);
        unlink($des_path);
        return true;
    }
    
    
    private function handleWriteBytes($cache, $start, $length) {
        $buff = file_get_contents("php://input");
        if ($length != strlen($buff)) {
            throw new MFilesException ( FXP::t ( MConst::PARAMS_ERROR ), MConst::HTTP_CODE_400 );
        }
        $handle = fopen($cache, "r+b");
        fseek($handle, $start);
        fwrite($handle, $buff);
        fclose($handle);
    }
    
    
    private function handleGetBlockCount($total_size, $block_size) {
        $count = $total_size / $block_size;
        $int_count = (int)$count;
        if ($count > $int_count) {
            $count += 1;
        }
        return (int)$count;
    }
    
    
    private function handleAssigns() {
        $etag       = $this->description['hash'];
        $des        = $this->description['blocks'];
        $block_size = $this->description['block_size'];
        
        header("HTTP/1.1 308 Resume Incomplete");
        header('ETag: ' . $etag);
        header('Content-Length: 0');
        header('X-Description: '. $des);
        header('X-Block-Length: '. $block_size);
        echo "0";
        exit(0);
    }
    
    
    private function hanleDescription($des_path, $cache, $total_size, $block_size, $hash) {
                                $blocks = "";
        $count = $this->handleGetBlockCount($total_size, $block_size);
        if (!file_exists($des_path)) {
            if (!MUtils::MkDirs(dirname($des_path))) {
                throw new MFilesException ( FXP::t ( MConst::INTERNAL_SERVER_ERROR ), MConst::HTTP_CODE_500 );
            }
            $description = array();
           
            for ($i = 0; $i < $count; $i++) {
                $blocks .= "0";
            }
            
            $description['hash']       = $hash;
            $description['total_size'] = $total_size;
            $description['block_size'] = $block_size;
            $description['blocks']     = $blocks;
            
            file_put_contents($des_path, serialize($description));
        } else {
            $str = file_get_contents($des_path);
            $description = unserialize($str);
        }
        
                if (!file_exists($cache) || filesize($cache) != $total_size) {
            $fp = fopen($cache, "wb");
            fseek($fp, $total_size - 1);
            fwrite($fp, "\0");
            fclose($fp);
            $blocks = "";
            for ($i = 0; $i < $count; $i++) {
                $blocks .= "0";
            }
            $description['blocks'] = $blocks;
        }
                                if ($total_size != $description['total_size']) {
            throw new MFilesException ( FXP::t ( MConst::PARAMS_ERROR ), MConst::HTTP_CODE_400 );
        }
        
        return  $description;
    }
}
?>